#define __SHORT_CIRCUIT_ET__
// Need to short circuit macros for min/max/floor/ceil to enable ET
#include "stencil.hpp"

#include "Wrapper.hpp"

#include "constants.h"

#include "./simple_expression_template/VAR.hpp"
#include "./simple_expression_template/TERM.hpp"
#include "./simple_expression_template/EXPRESSION.hpp"
#include "./simple_expression_template/ADD.hpp"
#include "./simple_expression_template/SUB.hpp"
#include "./simple_expression_template/MIN.hpp"
#include "./simple_expression_template/MAX.hpp"
#include "./simple_expression_template/CEIL.hpp"
#include "./simple_expression_template/FLOOR.hpp"
#include "./simple_expression_template/GE.hpp"
#include "./simple_expression_template/LE.hpp"

#undef __SHORT_CIRCUIT_ET__

template<int TILETAGDIM, int TASKDIM>
template<class DC>
void stencil_tuner<TILETAGDIM, TASKDIM>::depends
( const TileTag<TILETAGDIM, TASKDIM> & tt, 
  stencil_context<TILETAGDIM, TASKDIM> & c, DC & dc ) const {

  bool condt1 = false;
  bool condt2 = false;
  bool condt3 = false;
  void * wA;

  {
    constexpr VariableTerm<1> t1;
    constexpr VariableTerm<2> t2;
    constexpr VariableTerm<3> t3;
    constexpr VariableTerm<4> pT;
    constexpr VariableTerm<5> pM;
    constexpr VariableTerm<6> pN;
    constexpr VariableTerm<7> pP;

    std::tuple<int, int, int, int, int, int, int, int> 
      test_tuple_lb(0, tt[0], tt[1], tt[2], tt[3], tt[4], tt[5], tt[6]);

    auto lb_t1 = (t1>=ceil(-pN-15,16));
    auto ub_t1 = (t1<=floor(pT-3,16));
    auto lb_t2 = (t2>=max(t1,-t1-1));
    auto ub_t2 = (t2<=min(min(floor(-8*t1+pT-2,8),floor(8*t1+pN+7,8)),floor(pT+pN-2,16)));
    auto lb_t3 = (t3>=max(max(0,ceil(t1+t2-1,2)),ceil(16*t2-pN-14,16)));
    auto ub_t3 = (t3<=min(min(floor(pT+pN-2,16),floor(16*t2+pN+14,16)),floor(8*t1+8*t2+pN+15,16)));

    std::get<1>(test_tuple_lb) = tt[0]-1;
    condt1 |= !(lb_t1.Val(test_tuple_lb));
    condt1 |= !(ub_t1.Val(test_tuple_lb));
    condt1 |= !(lb_t2.Val(test_tuple_lb));
    condt1 |= !(ub_t2.Val(test_tuple_lb));
    condt1 |= !(lb_t3.Val(test_tuple_lb));
    condt1 |= !(ub_t3.Val(test_tuple_lb));

    std::get<1>(test_tuple_lb) = tt[0];
    std::get<2>(test_tuple_lb) = tt[1]-1;
    condt2 |= !(lb_t2.Val(test_tuple_lb));
    condt2 |= !(ub_t2.Val(test_tuple_lb));
    condt2 |= !(lb_t3.Val(test_tuple_lb));
    condt2 |= !(ub_t3.Val(test_tuple_lb));

    std::get<2>(test_tuple_lb) = tt[1];
    std::get<3>(test_tuple_lb) = tt[2]-1;
    condt3 |= !(lb_t3.Val(test_tuple_lb));
    condt3 |= !(ub_t3.Val(test_tuple_lb));
  }
  
  int64_t t1 = tt[0];
  int64_t t2 = tt[1];
  int64_t t3 = tt[2];
  int64_t pT = tt[3];
  int64_t pM = tt[4];
  int64_t pN = tt[5];
  int64_t pP = tt[6];
  if (!condt1) {
    dc.depends(c.M, TileTag<TILETAGDIM, TASKDIM>(t1-1,  t2,  t3,  pT,  pN,  pN,  pN));
  } 
  if (!condt2) {
    dc.depends(c.M, TileTag<TILETAGDIM, TASKDIM>(t1,  t2-1,  t3,  pT,  pN,  pN,  pN));
  } 
  if (!condt3) {
    dc.depends(c.M, TileTag<TILETAGDIM, TASKDIM>(t1,  t2,  t3-1,  pT,  pN,  pN,  pN));
  } 
}



template void 
stencil_tuner<TILETAGDIMMACRO, TASKDIMMACRO>::depends<CnC::Internal::step_delayer>
( const TileTag<TILETAGDIMMACRO, TASKDIMMACRO> & tt, 
  stencil_context<TILETAGDIMMACRO, TASKDIMMACRO> & c, 
  CnC::Internal::step_delayer &dc ) const;



template <int TILETAGDIM, int TASKDIM>
int64_t stencil_step<TILETAGDIM, TASKDIM>::execute
(const TileTag<TILETAGDIM, TASKDIM> &tt, stencil_context<TILETAGDIM, TASKDIM> & c) const
{

  bool condt1 = false;
  bool condt2 = false;
  bool condt3 = false;
  void * wA;

  {
    constexpr VariableTerm<1> t1;
    constexpr VariableTerm<2> t2;
    constexpr VariableTerm<3> t3;
    constexpr VariableTerm<4> pT;
    constexpr VariableTerm<5> pM;
    constexpr VariableTerm<6> pN;
    constexpr VariableTerm<7> pP;

    std::tuple<int, int, int, int, int, int, int, int> 
      test_tuple_lb(0, tt[0], tt[1], tt[2], tt[3], tt[4], tt[5], tt[6]);

    auto lb_t1 = (t1>=ceil(-pN-15,16));
    auto ub_t1 = (t1<=floor(pT-3,16));
    auto lb_t2 = (t2>=max(t1,-t1-1));
    auto ub_t2 = (t2<=min(min(floor(-8*t1+pT-2,8),floor(8*t1+pN+7,8)),floor(pT+pN-2,16)));
    auto lb_t3 = (t3>=max(max(0,ceil(t1+t2-1,2)),ceil(16*t2-pN-14,16)));
    auto ub_t3 = (t3<=min(min(floor(pT+pN-2,16),floor(16*t2+pN+14,16)),floor(8*t1+8*t2+pN+15,16)));

    std::get<1>(test_tuple_lb) = tt[0]-1;
    condt1 |= !(lb_t1.Val(test_tuple_lb));
    condt1 |= !(ub_t1.Val(test_tuple_lb));
    condt1 |= !(lb_t2.Val(test_tuple_lb));
    condt1 |= !(ub_t2.Val(test_tuple_lb));
    condt1 |= !(lb_t3.Val(test_tuple_lb));
    condt1 |= !(ub_t3.Val(test_tuple_lb));

    std::get<1>(test_tuple_lb) = tt[0];
    std::get<2>(test_tuple_lb) = tt[1]-1;
    condt2 |= !(lb_t2.Val(test_tuple_lb));
    condt2 |= !(ub_t2.Val(test_tuple_lb));
    condt2 |= !(lb_t3.Val(test_tuple_lb));
    condt2 |= !(ub_t3.Val(test_tuple_lb));

    std::get<2>(test_tuple_lb) = tt[1];
    std::get<3>(test_tuple_lb) = tt[2]-1;
    condt3 |= !(lb_t3.Val(test_tuple_lb));
    condt3 |= !(ub_t3.Val(test_tuple_lb));
  }

  {
    #include "definitions.h"
    int64_t t1 = tt[0];
    int64_t t2 = tt[1];
    int64_t t3 = tt[2];
    int64_t pT = tt[3];
    int64_t pM = tt[4];
    int64_t pN = tt[5];
    int64_t pP = tt[6];

    if (!condt1) {
      c.M.get(TileTag<TILETAGDIM, TASKDIM>(t1-1,  t2,  t3,  pT,  pN,  pN,  pN), wA);
    }

    if (!condt2) {
      c.M.get(TileTag<TILETAGDIM, TASKDIM>(t1,  t2-1,  t3,  pT,  pN,  pN,  pN), wA);
    } 
    if (!condt3) {
      c.M.get(TileTag<TILETAGDIM, TASKDIM>(t1,  t2,  t3-1,  pT,  pN,  pN,  pN), wA);
    } 

    if (condt1 && condt2 && condt3) {
      c.M.get(TileTag<TILETAGDIM, TASKDIM>(DUMMY_TILE_INDEX, DUMMY_TILE_INDEX, DUMMY_TILE_INDEX, pT,  pN, pN, pN), wA);
    }

    double (*M)[2][Npadded][Npadded][Npadded] = (double (*)[2][Npadded][Npadded][Npadded])(wA);

    int64_t t4, t5, t6, t7, t8, lbv, ubv;
    for (t4=max(max(max(0,ceild(t1+t2-124,125)),ceild(16*t2-pN-998,1000)),ceild(16*t3-pN-998,1000));t4<=min(min(min(min(floord(8*t1+pN+7,500),floord(pT+pN-2,1000)),floord(16*t2+pN+14,1000)),floord(16*t3+pN+14,1000)),floord(8*t1+8*t2+pN+15,1000));t4++) {
      for (t5=max(max(max(max(max(0,8*t1+8*t2),16*t1+1),16*t2-pN),16*t3-pN),1000*t4-pN);t5<=min(min(min(min(min(pT-2,16*t2+14),16*t3+14),1000*t4+998),8*t1+8*t2+15),16*t1+pN+15);t5++) {
        if (t5%2 == 0) {
          for (t6=max(max(16*t2,t5+1),-16*t1+2*t5-15);t6<=min(min(-16*t1+2*t5,16*t2+15),t5+pN);t6++) {
            for (t7=max(16*t3,t5+1);t7<=min(16*t3+15,t5+pN);t7++) {
              lbv=max(1000*t4,t5+1);
              ubv=min(1000*t4+999,t5+pN);
#pragma vector always
#pragma ivdep
              for (t8=lbv;t8<=ubv;t8++) {
                M[0][1][-t5+t6][-t5+t7][-t5+t8]=0.125*(M[0][0][-t5+t6+1][-t5+t7][-t5+t8]-2.0*M[0][0][-t5+t6][-t5+t7][-t5+t8]+M[0][0][-t5+t6-1][-t5+t7][-t5+t8])+0.125*(M[0][0][-t5+t6][-t5+t7+1][-t5+t8]-2.0*M[0][0][-t5+t6][-t5+t7][-t5+t8]+M[0][0][-t5+t6][-t5+t7-1][-t5+t8])+0.125*(M[0][0][-t5+t6][-t5+t7][-t5+t8-1]-2.0*M[0][0][-t5+t6][-t5+t7][-t5+t8]+M[0][0][-t5+t6][-t5+t7][-t5+t8+1])+M[0][0][-t5+t6][-t5+t7][-t5+t8];;
              }
            }
          }
        }else{
          for (t6=max(max(16*t2,t5+1),-16*t1+2*t5-15);t6<=min(min(-16*t1+2*t5,16*t2+15),t5+pN);t6++) {
            for (t7=max(16*t3,t5+1);t7<=min(16*t3+15,t5+pN);t7++) {
              lbv=max(1000*t4,t5+1);
              ubv=min(1000*t4+999,t5+pN);
#pragma ivdep
#pragma vector always
              for (t8=lbv;t8<=ubv;t8++) {
                M[0][0][-t5+t6][-t5+t7][-t5+t8]=0.125*(M[0][1][-t5+t6+1][-t5+t7][-t5+t8]-2.0*M[0][1][-t5+t6][-t5+t7][-t5+t8]+M[0][1][-t5+t6-1][-t5+t7][-t5+t8])+0.125*(M[0][1][-t5+t6][-t5+t7+1][-t5+t8]-2.0*M[0][1][-t5+t6][-t5+t7][-t5+t8]+M[0][1][-t5+t6][-t5+t7-1][-t5+t8])+0.125*(M[0][1][-t5+t6][-t5+t7][-t5+t8-1]-2.0*M[0][1][-t5+t6][-t5+t7][-t5+t8]+M[0][1][-t5+t6][-t5+t7][-t5+t8+1])+M[0][1][-t5+t6][-t5+t7][-t5+t8];;
              }
            }
          }
        }
      }
    }
    c.M.put(TileTag<TILETAGDIM, TASKDIM>(t1,  t2,  t3,  pT,  pM,  pN,  pP), M);  
  }  
  return CnC::CNC_Success;
}

// Template instantiation
template int64_t stencil_step<TILETAGDIMMACRO, TASKDIMMACRO>::execute
(const TileTag<TILETAGDIMMACRO, TASKDIMMACRO> &tt, 
 stencil_context<TILETAGDIMMACRO, TASKDIMMACRO> & c) const;

template <>
size_t cnc_tag_hash_compare< TileTag<TILETAGDIMMACRO, TASKDIMMACRO> >::hash(const TileTag<TILETAGDIMMACRO, TASKDIMMACRO>& tt) const
{
  return ( (tt[0]) << 19 ) + ( tt[1] << 7 ) + tt[2];
}

template <>
bool cnc_tag_hash_compare< TileTag<TILETAGDIMMACRO, TASKDIMMACRO> >::equal(const TileTag<TILETAGDIMMACRO, TASKDIMMACRO>& a, const TileTag<TILETAGDIMMACRO, TASKDIMMACRO>& b) const
{
  return a == b;
}
